package com.example.library;

import com.example.library.controller.BookController;
import com.example.library.entity.Book;
import com.example.library.entity.BorrowRecord;
import com.example.library.entity.Student;
import com.example.library.repository.BookRepository;
import com.example.library.repository.BorrowRecordRepository;
import com.example.library.repository.StudentRepository;
import com.example.library.service.BookService;
import com.example.library.service.BorrowService;

import jakarta.persistence.*;
import lombok.Data;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

@SpringBootApplication
public class LibrarySystem {
    public static void main(String[] args) {
        SpringApplication.run(LibrarySystem.class, args);
    }
}

@RestController
@RequestMapping("/api/books")
class BookController {
    @Autowired
    private BookService bookService;

    @GetMapping
    public List<Book> getAllBooks() {
        return bookService.getAllBooks();
    }

    @GetMapping("/search")
    public List<Book> searchBooks(@RequestParam String query) {
        return bookService.searchBooks(query);
    }

    @PostMapping
    public Book createBook(@RequestBody Book book) {
        return bookService.createBook(book);
    }

    @PutMapping("/{id}")
    public Book updateBook(@PathVariable Long id, @RequestBody Book book) {
        return bookService.updateBook(id, book);
    }

    @DeleteMapping("/{id}")
    public void deleteBook(@PathVariable Long id) {
        bookService.deleteBook(id);
    }
}

@Repository
interface BookRepository extends JpaRepository<Book, Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    Book findById(Long id);

    List<Book> findByTitleContainingOrIsbnContaining(String title, String isbn);
}

@Service
class BookService {
    @Autowired
    private BookRepository bookRepository;

    @Transactional
    public Book borrowBook(Long bookId) {
        Book book = bookRepository.findBookForUpdate(bookId);
        if (book.getStock() > 0) {
            book.setStock(book.getStock() - 1);
            return bookRepository.save(book);
        } else {
            throw new RuntimeException("Book is out of stock");
        }
    }

    @Transactional
    public void returnBook(Long bookId) {
        Book book = bookRepository.findBookForUpdate(bookId);
        book.setStock(book.getStock() + 1);
        bookRepository.save(book);
    }

    public List<Book> searchBooks(String query) {
        return bookRepository.findByTitleContainingOrIsbnContaining(query, query);
    }
}

@Service
class BorrowService {
    @Autowired
    private BookRepository bookRepository;

    @Autowired
    private StudentRepository studentRepository;

    @Autowired
    private BorrowRecordRepository borrowRecordRepository;

    @Transactional
    public BorrowRecord borrowBook(Long studentId, Long bookId) {
        Student student = studentRepository.findById(studentId)
                .orElseThrow(() -> new RuntimeException("学生不存在"));
        Book book = bookRepository.findById(bookId)
                .orElseThrow(() -> new RuntimeException("图书不存在"));

        if (book.getStock() <= 0) {
            throw new RuntimeException("库存不足");
        }

        book.setStock(book.getStock() - 1);
        bookRepository.save(book);

        BorrowRecord record = new BorrowRecord();
        record.setStudent(student);
        record.setBook(book);
        record.setBorrowTime(LocalDateTime.now());
        return borrowRecordRepository.save(record);
    }

    @Transactional
    public void returnBook(Long recordId) {
        BorrowRecord record = borrowRecordRepository.findById(recordId)
                .orElseThrow(() -> new RuntimeException("借阅记录不存在"));

        if (record.getIsReturned()) {
            throw new RuntimeException("该书已归还");
        }

        Duration duration = Duration.between(record.getBorrowTime(), LocalDateTime.now());
        long daysOverdue = duration.toDays() - 30; // 假设借期为30天
        if (daysOverdue > 0) {
            record.setFineAmount(daysOverdue * 0.5); // 每天罚款0.5元
        }

        record.setIsReturned(true);
        record.setReturnTime(LocalDateTime.now());
        borrowRecordRepository.save(record);

        Book book = record.getBook();
        book.setStock(book.getStock() + 1);
        bookRepository.save(book);
    }
}

@Data
@Entity
@Table(name = "books")
class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String isbn;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String author;

    @Column(nullable = false)
    private Integer stock;
}

@Data
@Entity
@Table(name = "borrow_records")
class BorrowRecord {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "student_id", nullable = false)
    private Student student;

    @ManyToOne
    @JoinColumn(name = "book_id", nullable = false)
    private Book book;

    @Column(nullable = false)
    private LocalDateTime borrowTime;

    private LocalDateTime returnTime;

    private Boolean isReturned = false;

    private Double fineAmount;
}

@Data
@Entity
@Table(name = "students")
class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String studentId;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String grade;

    @Column(nullable = false)
    private String major;
}